package gins

import (
	"encoding/json"
	stdErrors "errors"
	"fmt"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/mars/internal/pkg/code"
	"github.com/mars/third_part/fork/errors"
)

// Page 分页相关的参数， 在该工程中，分页参数都放到请求参数，即：
//
//	/users?pageSize=<>pageNo=<>
type Page struct {
	PageSize int `form:"pageSize"`
	PageNo   int `form:"pageNo"`
}

func (p Page) CalculateSkip() int {
	return (p.PageNo - 1) * p.PageSize
}

var (
	ErrQueryParamsInvalid = stdErrors.New("path variable is invalid")
)

// ParsePage 从 gin.Context 中解析出 page 信息
func ParsePage(ctx *gin.Context) (page Page, err error) {
	if err = ctx.BindQuery(&page); err != nil {
		return
	}

	if page.PageSize <= 0 {
		page.PageSize = 10
	}
	if page.PageNo <= 0 {
		page.PageNo = 1
	}

	return
}

// 解析 postData 参数
// postData应该是编码后的数据
func ParsePostData(ctx *gin.Context, data interface{}) error {
	postData := ctx.Query("postData")
	return json.Unmarshal([]byte(postData), data)
}

func QueryInt(ctx *gin.Context, key string) (int, error) {
	var (
		value  = ctx.Query(key)
		result = 0
		err    error
	)

	if len(value) == 0 {
		return result, fmt.Errorf("%s, value is null", ErrQueryParamsInvalid)
	}
	if result, err = strconv.Atoi(value); err != nil {
		return result, fmt.Errorf("%s, %v", ErrQueryParamsInvalid, err)
	}

	return result, nil
}

func QueryInt64(ctx *gin.Context, key string) (int64, error) {
	var (
		value  = ctx.Query(key)
		result int64
		err    error
	)

	if len(value) == 0 {
		return result, fmt.Errorf("%w, value is null", ErrQueryParamsInvalid)
	}

	if result, err = strconv.ParseInt(value, 10, 64); err != nil {
		return result, fmt.Errorf("%w, %s", ErrQueryParamsInvalid, err)
	}

	return result, nil
}

func QueryBool(ctx *gin.Context, key string) (bool, error) {
	var (
		value  = ctx.Query(key)
		result = false
		err    error
	)

	if len(value) == 0 {
		return result, fmt.Errorf("%w, value is null", ErrQueryParamsInvalid)
	}
	if result, err = strconv.ParseBool(value); err != nil {
		return result, fmt.Errorf("%w, %v", ErrQueryParamsInvalid, err)
	}

	return result, nil
}

func ParamInt(ctx *gin.Context, key string) (v int, err error) {
	return strconv.Atoi(ctx.Param(key))
}

func ParamInt64(ctx *gin.Context, key string) (int64, error) {
	valStr := ctx.Param(key)
	return strconv.ParseInt(valStr, 10, 64)
}

func ParamInt8(ctx *gin.Context, key string) (v int8, err error) {
	var value int
	if value, err = strconv.Atoi(ctx.Param(key)); err != nil {
		err = errors.WithCodeE(err, code.ErrParamsInvalid)
		return
	}
	if value > 2<<8-1 {
		err = errors.WithCode(code.ErrParamsInvalid, "params should be less %d", 2<<8)
		return
	}
	return int8(value), nil
}
